package com.alstudio.utils.android.net.upload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.os.AsyncTask;

import com.alstudio.utils.android.ALFileManager;
import com.alstudio.utils.android.ALSdcardManager;
import com.alstudio.utils.android.AndroidUtils;
import com.alstudio.utils.log.Mylog;

public class ALHttpUploadTask extends AsyncTask<Void, Integer, Long> {

	private ALHttpUploadListener mListener;

	private File file;
	private String path;
	private String fileName;
	private String url;
	private Throwable exception;
	private Context mContext;
	private HttpURLConnection mConnection = null;

	private long uploadSize;
	private long previousFileSize;
	private long totalSize;
	private int uploadPercent;
	private long networkSpeed; // 网速
	private long previousTime;
	private long totalTime;

	private String filePath;

	private boolean interrupt = false;
	private ALHttpUploadErrorCode httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_UNKONW;

	private final int AL_HTTP_TIME_OUT = 30000;
	private final int BUFFER_SIZE = 1024 * 8;

	private OutputStream mOutPutStream;
	private int responseCode = 200;

	public ALHttpUploadTask(Context context, String url, String path,
			ALHttpUploadListener listener) {
		super();
		this.path = path;
		mContext = context;
		this.url = url;
		mListener = listener;
		this.filePath = path;
	}

	/**
	 * 设定上传事件监听器
	 * 
	 * @param listener
	 */
	public void setUploadStateListener(ALHttpUploadListener listener) {
		this.mListener = listener;
	}

	/**
	 * 获取上传事件监听器
	 * 
	 * @return
	 */
	public ALHttpUploadListener getUploadStateListener() {
		return this.mListener;
	}

	/**
	 * 获取上传url
	 * 
	 * @return
	 */
	public String getUrl() {
		return url;
	}

	/**
	 * 获取上传进度百分比
	 * 
	 * @return
	 */
	public int getUploadPercent() {
		return uploadPercent;
	}

	/**
	 * 获取已上传文件大小
	 * 
	 * @return
	 */
	public long getuploadSize() {
		return uploadSize + previousFileSize;
	}

	/**
	 * 获取文件总大小
	 * 
	 * @return
	 */
	public long getTotalSize() {
		return totalSize;
	}

	/**
	 * 获取上传速度
	 * 
	 * @return
	 */
	public long getUploadSpeed() {
		return this.networkSpeed;
	}

	/**
	 * 获取上传成功后花费的总时长
	 * 
	 * @return
	 */
	public long getTotalTime() {
		return this.totalTime;
	}

	/**
	 * 获取文件路径
	 * 
	 * @return
	 */
	public String getFilePath() {
		return this.filePath;
	}

	/**
	 * 获取服务器响应码
	 * 
	 * @return
	 */
	public int getResponseCode() {
		return responseCode;
	}

	@Override
	protected void onPreExecute() {
		previousTime = System.currentTimeMillis();
		if (mListener != null)
			mListener.onUploadStart(this);
	}

	@Override
	protected Long doInBackground(Void... params) {
		try {
			return startUpload();
		} catch (Exception e) {
			if (mConnection != null) {
				mConnection.disconnect();
			}
			exception = e;
			return null;
		}
	}

	@Override
	protected void onProgressUpdate(Integer... progress) {
		if (progress.length > 1) {
			totalSize = progress[1];
			if (totalSize == -1) {
				if (mListener != null)
					mListener.onUploadFail(this, httpErrorStausCode);
			} else {

			}
		} else {
			totalTime = System.currentTimeMillis() - previousTime;
			uploadSize = progress[0];
			uploadPercent = (int) ((uploadSize + previousFileSize) * 100 / totalSize);
			networkSpeed = uploadSize / totalTime;
			if (mListener != null)
				mListener.onUpdateProgress(this);
		}
	}

	@Override
	protected void onPostExecute(Long result) {
		if (interrupt) {
			if (mListener != null)
				mListener.onUploadFail(this, httpErrorStausCode);
			return;
		}

		if (exception != null) {
			if (mListener != null)
				mListener.onUploadFail(this, httpErrorStausCode);
		} else {
			if (mListener != null)
				mListener.onUploadFinish(this);
		}
	}

	@Override
	public void onCancelled() {
		super.onCancelled();
		interrupt = true;
	}

	private long startUpload() throws Exception {

		// SD卡不存在
		if (!ALSdcardManager.isExtStorageAvailable()) {
			interrupt = true;
			httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_NO_EXTENAL_STORAGE;
			return 0l;
		}

		// 文件不存在
		if (!ALFileManager.isFileExsit(path)) {
			interrupt = true;
			httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_FILE_NOT_FOUND;
			return 0l;
		}

		mConnection = buildHttpUrlConnection(url);

		file = new File(path);

		filePath = file.getAbsolutePath();

		// 获取文件大小
		totalSize = ALFileManager.getFileSize(file);
		publishProgress(0, (int) totalSize);

		InputStream input = null;

		mOutPutStream = mConnection.getOutputStream();

		input = new FileInputStream(file);

		int bytesCopied = 0;

		try {
			// 写数据到服务器
			bytesCopied = copy(input, mOutPutStream);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			// 拷贝数据的时候出现异常

			// 关闭输入流
			if (input != null) {
				input.close();
			}
			// 关闭输出
			if (mOutPutStream != null) {
				mOutPutStream.close();
			}

			mConnection.disconnect();
			mConnection = null;

			return 0l;
		}

		responseCode = mConnection.getResponseCode();

		if (HttpURLConnection.HTTP_OK != responseCode) {
			interrupt = true;
			httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_RESPONSE_CODE;
			return 0l;
		}

		if ((bytesCopied) != totalSize && totalSize != -1 && !interrupt) {
			throw new IOException("upload incomplete: " + bytesCopied + " != "
					+ totalSize);
		}

		mOutPutStream.close();
		mConnection.disconnect();
		input.close();
		mConnection = null;
		Mylog.d("upload completed");
		return bytesCopied;
	}

	public int copy(InputStream input, OutputStream out) throws Exception {
		byte[] buffer = new byte[BUFFER_SIZE];

		BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);

		int count = 0, n = 0;
		long errorBlockTimePreviousTime = -1, expireTime = 0;
		try {
			while (!interrupt) {
				try {
					n = in.read(buffer, 0, BUFFER_SIZE);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					interrupt = true;
					// 读取本地文件出错
					httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_IO_EXCEPTION;
					break;

				}
				if (n == -1) {
					break;
				}

				out.write(buffer, 0, n);
				out.flush();

				count += n;

				publishProgress(count);

				if (!AndroidUtils.isNetworkConnected(mContext)) {
					interrupt = true;
					// 无网络
					httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_NO_INTENET_CONNECTION;
					break;
				}

				if (networkSpeed == 0) {
					if (errorBlockTimePreviousTime > 0) {
						expireTime = System.currentTimeMillis()
								- errorBlockTimePreviousTime;
						if (expireTime > AL_HTTP_TIME_OUT) {
							// 网络超时
							httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_TIME_OUT;
							interrupt = true;
						}
					} else {
						errorBlockTimePreviousTime = System.currentTimeMillis();
					}
				} else {
					expireTime = 0;
					errorBlockTimePreviousTime = -1;
				}
			}
		} finally {
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				// 未知错误
				httpErrorStausCode = ALHttpUploadErrorCode.AL_HTTP_UPLOAD_TYPE_ERROR_UNKONW;
			}
		}
		return count;
	}

	private HttpURLConnection buildHttpUrlConnection(String urlString)
			throws IOException {
		Mylog.d("building http connection: " + urlString);
		URL url = new URL(urlString);

		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setReadTimeout(AL_HTTP_TIME_OUT /* milliseconds */);
		conn.setConnectTimeout(AL_HTTP_TIME_OUT /* milliseconds */);
		conn.setDoInput(true);
		conn.setDoOutput(true);

		conn.setRequestProperty("keepAlive", "false");
		conn.setRequestProperty("Connection", "close");

		mConnection.setRequestProperty("content-type",
				"application/octet-stream");

		Mylog.d("http connection establised");
		return conn;
	}

	public enum ALHttpUploadErrorCode {
		// 上传成功
		AL_HTTP_UPLOAD_TYPE_ERROR_NONE,
		// 未挂载外部存储器
		AL_HTTP_UPLOAD_TYPE_ERROR_NO_EXTENAL_STORAGE,
		// 网络不可用
		AL_HTTP_UPLOAD_TYPE_ERROR_NO_INTENET_CONNECTION,
		// 超时
		AL_HTTP_UPLOAD_TYPE_ERROR_TIME_OUT,
		// 读取本地文件IO异常
		AL_HTTP_UPLOAD_TYPE_ERROR_IO_EXCEPTION,
		// 文件不存在
		AL_HTTP_UPLOAD_TYPE_ERROR_FILE_NOT_FOUND,
		// 服务器返回码异常
		AL_HTTP_UPLOAD_TYPE_ERROR_RESPONSE_CODE,
		// 未知错误
		AL_HTTP_UPLOAD_TYPE_ERROR_UNKONW,
	}
}
